package standard.util;

import com.alibaba.fastjson.JSON;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import standard.*;
import standard.transferObjects.*;

import java.io.*;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

/**
 * Create by Lingo
 */

public class Generator {

    private String tableName;

    private PackageConfig packageConfig;

    private Configuration configuration = new Configuration();

    private TableInfo tableInfo;

    private String templatePath = "src/main/resources/templates";

    private void init() throws SQLException, ClassNotFoundException, IOException {
        tableInfo = SQLUtils.getTableInfo(tableName);
        configuration.setDirectoryForTemplateLoading(new File(templatePath));
        File file = new File(packageConfig.getMapperDirPath());
        if (!file.exists()) {
            if (!file.mkdirs()) {
                throw new RuntimeException("Cannot create the mapper dir");
            }
        }
        file = new File(packageConfig.getControllerDirPath());
        if (!file.exists()) {
            if (!file.mkdirs()) {
                throw new RuntimeException("Cannot create the controller dir");
            }
        }
        file = new File(packageConfig.getServiceImplDirPath());
        if (!file.exists()) {
            if (!file.mkdirs()) {
                throw new RuntimeException("Cannot create the serviceImpl dir");
            }
        }
        file = new File(packageConfig.getModelDirPath());
        if (!file.exists()) {
            if (!file.mkdirs()) {
                throw new RuntimeException("Cannot create the model dir");
            }
        }
    }

    public Generator(String tableName,PackageConfig packageConfig) {
        this.packageConfig = packageConfig;
        this.tableName = tableName;
    }

    public void generate() throws SQLException, IOException, ClassNotFoundException {
        init();
        generateModel();
        generateService();
        generateServiceImpl();
        generateMapper();
        generateMapperXml();
        generateControllerForXybb();
    }

    private void generateModel() {
        Writer out = null;
        try {
            ModelTO modelTO = new ModelTO();
            modelTO.setEntityClassName(packageConfig.getModelClassName());
            modelTO.setPackageName(packageConfig.getModelPackage());
            modelTO.setApiModel(tableInfo.getRemark());
            for (ColumnInfo columnInfo : tableInfo.getColumnInfoList()) {
                StandardVar var = Utils.colInfo2StandardVar(columnInfo);
                modelTO.getFieldList().add(var);
            }
            modelTO.setImportList(getModelImportList());
            Template template = configuration.getTemplate(packageConfig.getModelTemplateName());
            File file = new File(packageConfig.getModelFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(modelTO), out);
            System.out.println("实体类生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<String> getModelImportList() {
        LinkedList<String> list = new LinkedList<>();
        list.add("io.swagger.annotations.ApiModel");
        list.add("io.swagger.annotations.ApiModelProperty");
        list.add("java.io.Serializable");
        for (ColumnInfo columnInfo : tableInfo.getColumnInfoList()) {
            String className = columnInfo.getJavaClassName();
            // 排除默认已经加载的包 java.lang.*
            if (!className.startsWith("java.lang") && !className.equals("java.sql.Date") && !list.contains(className)) {
                list.add(className);
            }
            // 添加@NotNull 等注解
            String annotationPath = getNullAnnotationPath(columnInfo);
            if (null != annotationPath && !list.contains(annotationPath)) {
                list.add(annotationPath);
            }
        }
        return list;
    }

    private String getNullAnnotationPath(ColumnInfo columnInfo) {
        String javaClassName = Utils.handleClassName(columnInfo.getJavaClassName());
        if (0 == columnInfo.getIsNullable() && 0 == columnInfo.getIsPrimary() && "Fcreate_time" != columnInfo.getName()
                && "Fmodify_time" != columnInfo.getName()) {
            if (javaClassName.equals("String")) {
                return NullAnnotationEnum.getPackageNameByCode("NotBlank");
            }
            if (javaClassName.contains("List")) {
                return NullAnnotationEnum.getPackageNameByCode("NotEmpty");
            }
            return NullAnnotationEnum.getPackageNameByCode("NotNull");
        }
        return null;
    }

    private void generateService() {
        Configuration configuration = new Configuration();
        Writer out = null;
        try {
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            ServiceTO serviceTO = new ServiceTO();
            serviceTO.setServiceName(packageConfig.getServiceClassName());
            serviceTO.setImportList(getServiceImportList());
            serviceTO.setPackageName(packageConfig.getServicePackage());
            serviceTO.setEntityClassName(packageConfig.getModelClassName());
            serviceTO.setPrimaryKey(tableInfo.getPrimaryKey());
            Template template = configuration.getTemplate(packageConfig.getServiceTemplateName());
            File file = new File(packageConfig.getServiceFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(serviceTO), out);
            System.out.println("Service层生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<String> getServiceImportList() {
        LinkedList<String> importList = new LinkedList<>();
        importList.add(packageConfig.getModelClassPath());
        importList.add("java.util.List");
        importList.add("java.util.Map");
        return importList;
    }

    private void generateServiceImpl() {
        Configuration configuration = new Configuration();
        Writer out = null;
        try {
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            Template template = configuration.getTemplate(packageConfig.getServiceImpTemplateName());
            ServiceImplTO serviceImplTO = new ServiceImplTO();
            serviceImplTO.setEntityClassName(packageConfig.getModelClassName());
            serviceImplTO.setImportList(getServiceImplImportList());
            serviceImplTO.setMapperName(packageConfig.getMapperClassName());
            serviceImplTO.setPackageName(packageConfig.getServiceImplPackage());
            serviceImplTO.setPrimaryKey(tableInfo.getPrimaryKey());
            serviceImplTO.setServiceName(packageConfig.getServiceClassName());
            serviceImplTO.setServiceImplName(packageConfig.getServiceImplClassName());
            File file = new File(packageConfig.getServiceImplFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(serviceImplTO), out);
            System.out.println("ServiceImpl层生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<String> getServiceImplImportList() {
        LinkedList<String> importList = new LinkedList<>();
        importList.add(packageConfig.getModelClassPath());
        importList.add(packageConfig.getServiceClassPath());
        importList.add(packageConfig.getMapperClassPath());
        importList.add("java.util.List");
        importList.add("java.util.Map");
        importList.add("java.util.LinkedHashMap");
        importList.add("com.github.pagehelper.Page");
        importList.add("com.github.pagehelper.PageHelper");
        importList.add("org.springframework.beans.factory.annotation.Autowired");
        importList.add("org.springframework.stereotype.Service");
        return importList;
    }

    private void generateMapper() {
        Configuration configuration = new Configuration();
        Writer out = null;
        try {
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            MapperTO mapperTO = new MapperTO();
            mapperTO.setMapperName(packageConfig.getMapperClassName());
            mapperTO.setImportList(getMapperImportList());
            mapperTO.setPackageName(packageConfig.getMapperPackage());
            mapperTO.setEntityClassName(packageConfig.getModelClassName());
            mapperTO.setPrimaryKey(tableInfo.getPrimaryKey());
            Template template = configuration.getTemplate(packageConfig.getMapperTemplateName());
            File file = new File(packageConfig.getMapperFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(mapperTO), out);
            System.out.println("Mapper层生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<String> getMapperImportList() {
        LinkedList<String> importList = new LinkedList<>();
        importList.add(packageConfig.getModelClassPath());
        importList.add("java.util.List");
        importList.add("java.util.Map");
        return importList;
    }


    private void generateMapperXml() {
        Configuration configuration = new Configuration();
        Writer out = null;
        try {
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            MapperXmlTO mapperXmlTO = new MapperXmlTO();
            mapperXmlTO.setPrimaryKey(new ColAndVar(tableInfo.getPrimaryCol()));
            List<ColAndVar> cols = new LinkedList<>();
            for (ColumnInfo otherFieldCol : tableInfo.getOtherFieldCol()) {
                cols.add(new ColAndVar(otherFieldCol));
            }
            mapperXmlTO.setOtherField(cols);
            mapperXmlTO.setTableName(tableInfo.getName());
            mapperXmlTO.setPathToMapperClass(packageConfig.getMapperClassPath());
            mapperXmlTO.setPathToModelClass(packageConfig.getModelClassPath());
            Template template = configuration.getTemplate(packageConfig.getMapperXmlTemplateName());
            File file = new File(packageConfig.getMapperXmlFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(mapperXmlTO), out);
            System.out.println("MapperXML层生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private void generateController() {
        Configuration configuration = new Configuration();
        Writer out = null;
        try {
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            ControllerTO controllerTO = new ControllerTO();
            controllerTO.setPackageName(packageConfig.getControllerPackage());
            controllerTO.setModelName(packageConfig.getModelName());
            controllerTO.setPathToEntityClass(packageConfig.getModelClassPath());
            controllerTO.setPrimaryKey(tableInfo.getPrimaryKey());
            controllerTO.setServiceName(packageConfig.getServiceClassName());
            controllerTO.setApiName(tableInfo.getRemark());
            controllerTO.setPrimaryKey(tableInfo.getPrimaryKey());
            controllerTO.setControllerName(packageConfig.getControllerClassName());
            controllerTO.setEntityClassName(packageConfig.getModelClassName());
            controllerTO.setImportList(getControllerImportList());
            Template template = configuration.getTemplate(packageConfig.getControllerTemplateName());
            File file = new File(packageConfig.getControllerFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(controllerTO), out);
            System.out.println("Controller层生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<String> getControllerImportList() {
        LinkedList<String> importList = new LinkedList<>();
        importList.add(packageConfig.getModelClassPath());
        importList.add(packageConfig.getServiceClassPath());
        importList.add("io.swagger.annotations.Api");
        importList.add("io.swagger.annotations.ApiOperation");
        importList.add("org.springframework.beans.factory.annotation.Autowired");
        importList.add("org.springframework.web.bind.annotation.*");
        importList.add("java.util.List");
        importList.add("java.util.Map");
        importList.add("javax.validation.Valid");
        return importList;
    }

    private void generateControllerForXybb() {
        Configuration configuration = new Configuration();
        Writer out = null;
        try {
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            ControllerTO controllerTO = new ControllerTO();
            controllerTO.setPackageName(packageConfig.getControllerPackage());
            controllerTO.setModelName(packageConfig.getModelName());
            controllerTO.setPathToEntityClass(packageConfig.getModelClassPath());
            controllerTO.setPrimaryKey(tableInfo.getPrimaryKey());
            controllerTO.setServiceName(packageConfig.getServiceClassName());
            controllerTO.setApiName(tableInfo.getRemark());
            controllerTO.setPrimaryKey(tableInfo.getPrimaryKey());
            controllerTO.setControllerName(packageConfig.getControllerClassName());
            controllerTO.setEntityClassName(packageConfig.getModelClassName());
            controllerTO.setImportList(getControllerForXybbImportList());
            Template template = configuration.getTemplate(packageConfig.getControllerForXybbTemplateName());
            File file = new File(packageConfig.getControllerFilePath());
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            template.process(JSON.toJSON(controllerTO), out);
            System.out.println("Controller层生成成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }

    private List<String> getControllerForXybbImportList() {
        LinkedList<String> importList = new LinkedList<>();
        importList.add(packageConfig.getModelClassPath());
        importList.add(packageConfig.getServiceClassPath());
        importList.add("com.xingyun.xybb.common.constant.XyRetCodeConstant");
        importList.add("com.xingyun.xybb.common.response.XyResponseEntity");
        importList.add("com.xingyun.xybb.swagger.annotation.XyApiJsonResponse");
        importList.add("com.xingyun.xybb.swagger.annotation.XyApiJsonResponses");
        importList.add("io.swagger.annotations.Api");
        importList.add("io.swagger.annotations.ApiOperation");
        importList.add("org.springframework.beans.factory.annotation.Autowired");
        importList.add("org.springframework.http.ResponseEntity");
        importList.add("org.springframework.web.bind.annotation.*");
        importList.add("java.util.Map");
        importList.add("javax.validation.Valid");
        return importList;
    }
}
